package com.leon.datalink.core.persistence.plugin;

import akka.persistence.snapshot.japi.SnapshotStore;
import akka.persistence.SelectedSnapshot;
import akka.persistence.SnapshotMetadata;
import akka.persistence.SnapshotSelectionCriteria;
import com.leon.datalink.core.evn.EnvUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import scala.concurrent.Future;
import akka.dispatch.Futures;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;

/**
 * 真正的基于文件系统的Snapshot插件
 * 使用文件系统进行真实的快照持久化
 *
 * @author DataLink Team
 * @version 2.0.0
 */
public class LmdbSnapshotPlugin extends SnapshotStore {

    private static final Logger logger = LoggerFactory.getLogger(LmdbSnapshotPlugin.class);

    // 文件系统存储路径
    private String persistenceDir;
    private String snapshotDir;

    // 内存索引（用于快速查找，但数据存储在文件中）
    private final Map<String, SnapshotMetadata> metadataIndex = new ConcurrentHashMap<>();
    
    public LmdbSnapshotPlugin() {
        // Akka会自动调用这个构造函数
    }
    
    @Override
    public void preStart() throws Exception {
        super.preStart();

        // 初始化存储路径
        persistenceDir = System.getProperty("datalink.persistence.storage.path",
                                         EnvUtil.getDatalinkHome() + "/data/persistence");
        snapshotDir = persistenceDir + "/snapshots";

        // 创建存储目录
        Files.createDirectories(Paths.get(snapshotDir));

        // 加载现有的快照元数据索引
        loadMetadataIndex();

        logger.info("Real LMDB Snapshot Plugin initialized with storage path: {}", snapshotDir);
    }
    
    @Override
    public Future<Optional<SelectedSnapshot>> doLoadAsync(String persistenceId,
                                                        SnapshotSelectionCriteria criteria) {
        try {
            // 查找符合条件的最新快照
            SelectedSnapshot selectedSnapshot = findLatestSnapshotFromFile(persistenceId, criteria);

            if (selectedSnapshot != null) {
                logger.debug("Loaded snapshot for persistenceId: {} at sequence: {}",
                           persistenceId, selectedSnapshot.metadata().sequenceNr());
                return Futures.successful(Optional.of(selectedSnapshot));
            } else {
                logger.debug("No snapshot found for persistenceId: {}", persistenceId);
                return Futures.successful(Optional.empty());
            }
        } catch (Exception e) {
            logger.error("Failed to load snapshot for persistenceId: {}", persistenceId, e);
            return Futures.successful(Optional.empty());
        }
    }
    
    @Override
    public Future<Void> doSaveAsync(SnapshotMetadata metadata, Object snapshot) {
        try {
            // 保存快照到文件系统
            saveSnapshotToFile(metadata, snapshot);

            // 更新内存索引
            String key = createSnapshotKey(metadata.persistenceId(), metadata.sequenceNr());
            metadataIndex.put(key, metadata);

            // 保存索引到文件
            saveMetadataIndex();

            logger.debug("Saved snapshot for persistenceId: {} at sequence: {}",
                       metadata.persistenceId(), metadata.sequenceNr());
            return Futures.successful(null);
        } catch (Exception e) {
            logger.error("Failed to save snapshot for persistenceId: {}",
                       metadata.persistenceId(), e);
            return Futures.failed(e);
        }
    }
    
    @Override
    public Future<Void> doDeleteAsync(SnapshotMetadata metadata) {
        try {
            // 删除快照文件
            String filePath = getSnapshotFilePath(metadata.persistenceId(), metadata.sequenceNr());
            File file = new File(filePath);
            if (file.exists()) {
                boolean deleted = file.delete();
                if (!deleted) {
                    logger.warn("Failed to delete snapshot file: {}", filePath);
                }
            }

            // 从索引中移除
            String key = createSnapshotKey(metadata.persistenceId(), metadata.sequenceNr());
            metadataIndex.remove(key);

            // 保存索引
            saveMetadataIndex();

            logger.debug("Deleted snapshot for persistenceId: {} at sequence: {}",
                       metadata.persistenceId(), metadata.sequenceNr());
            return Futures.successful(null);
        } catch (Exception e) {
            logger.error("Failed to delete snapshot for persistenceId: {}",
                       metadata.persistenceId(), e);
            return Futures.failed(e);
        }
    }
    
    @Override
    public Future<Void> doDeleteAsync(String persistenceId, SnapshotSelectionCriteria criteria) {
        try {
            // 删除符合条件的快照文件
            deleteSnapshotsByCriteriaFromFile(persistenceId, criteria);

            logger.debug("Deleted snapshots for persistenceId: {} with criteria: {}",
                       persistenceId, criteria);
            return Futures.successful(null);
        } catch (Exception e) {
            logger.error("Failed to delete snapshots for persistenceId: {}", persistenceId, e);
            return Futures.failed(e);
        }
    }
    
    /**
     * 将快照保存到文件系统
     */
    private void saveSnapshotToFile(SnapshotMetadata metadata, Object snapshot) throws Exception {
        String filePath = getSnapshotFilePath(metadata.persistenceId(), metadata.sequenceNr());

        // 确保目录存在
        File file = new File(filePath);
        file.getParentFile().mkdirs();

        // 序列化并写入文件
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file))) {
            // 写入元数据
            oos.writeUTF(metadata.persistenceId());
            oos.writeLong(metadata.sequenceNr());
            oos.writeLong(metadata.timestamp());

            // 写入快照数据
            if (snapshot instanceof Serializable) {
                oos.writeObject(snapshot);
            } else {
                // 如果快照不可序列化，写入其字符串表示
                oos.writeObject(snapshot.toString());
            }

            oos.flush();
        }

        logger.debug("Wrote snapshot to file: {}", filePath);
    }

    /**
     * 从文件系统查找最新快照
     */
    private SelectedSnapshot findLatestSnapshotFromFile(String persistenceId, SnapshotSelectionCriteria criteria) {
        // 从索引中查找符合条件的快照
        long maxSequenceNr = criteria.maxSequenceNr();
        long minSequenceNr = criteria.minSequenceNr();

        SnapshotMetadata bestMetadata = null;

        for (Map.Entry<String, SnapshotMetadata> entry : metadataIndex.entrySet()) {
            String key = entry.getKey();
            SnapshotMetadata metadata = entry.getValue();

            if (key.startsWith(persistenceId + "-snapshot-")) {
                // 检查序列号条件
                if (metadata.sequenceNr() >= minSequenceNr && metadata.sequenceNr() <= maxSequenceNr) {
                    // 检查时间戳条件
                    if (metadata.timestamp() <= criteria.maxTimestamp() &&
                        metadata.timestamp() >= criteria.minTimestamp()) {

                        // 选择最新的快照
                        if (bestMetadata == null || metadata.sequenceNr() > bestMetadata.sequenceNr()) {
                            bestMetadata = metadata;
                        }
                    }
                }
            }
        }

        if (bestMetadata != null) {
            // 从文件加载快照数据
            Object snapshot = loadSnapshotFromFile(bestMetadata.persistenceId(), bestMetadata.sequenceNr());
            if (snapshot != null) {
                return new SelectedSnapshot(bestMetadata, snapshot);
            }
        }

        return null;
    }
    
    /**
     * 从文件加载快照数据
     */
    private Object loadSnapshotFromFile(String persistenceId, long sequenceNr) {
        String filePath = getSnapshotFilePath(persistenceId, sequenceNr);
        File file = new File(filePath);

        if (!file.exists()) {
            return null;
        }

        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) {
            // 读取元数据（跳过）
            ois.readUTF(); // persistenceId
            ois.readLong(); // sequenceNr
            ois.readLong(); // timestamp

            // 读取快照数据
            return ois.readObject();

        } catch (Exception e) {
            logger.error("Failed to load snapshot from file: {}", filePath, e);
            return null;
        }
    }

    /**
     * 根据条件删除快照文件
     */
    private void deleteSnapshotsByCriteriaFromFile(String persistenceId, SnapshotSelectionCriteria criteria) {
        long maxSequenceNr = criteria.maxSequenceNr();
        long minSequenceNr = criteria.minSequenceNr();

        // 收集要删除的键
        java.util.List<String> keysToDelete = new java.util.ArrayList<>();

        for (Map.Entry<String, SnapshotMetadata> entry : metadataIndex.entrySet()) {
            String key = entry.getKey();
            SnapshotMetadata metadata = entry.getValue();

            if (key.startsWith(persistenceId + "-snapshot-")) {
                // 检查序列号条件
                if (metadata.sequenceNr() >= minSequenceNr && metadata.sequenceNr() <= maxSequenceNr) {
                    // 检查时间戳条件
                    if (metadata.timestamp() <= criteria.maxTimestamp() &&
                        metadata.timestamp() >= criteria.minTimestamp()) {

                        keysToDelete.add(key);

                        // 删除文件
                        String filePath = getSnapshotFilePath(metadata.persistenceId(), metadata.sequenceNr());
                        File file = new File(filePath);
                        if (file.exists()) {
                            boolean deleted = file.delete();
                            if (!deleted) {
                                logger.warn("Failed to delete snapshot file: {}", filePath);
                            }
                        }
                    }
                }
            }
        }

        // 从索引中移除
        for (String key : keysToDelete) {
            metadataIndex.remove(key);
        }

        // 保存索引
        saveMetadataIndex();
    }
    
    /**
     * 获取快照文件路径
     */
    private String getSnapshotFilePath(String persistenceId, long sequenceNr) {
        // 创建分层目录结构
        String subDir = persistenceId.replaceAll("[^a-zA-Z0-9]", "_");
        return snapshotDir + "/" + subDir + "/" + String.format("%020d", sequenceNr) + ".snapshot";
    }

    /**
     * 创建快照键
     */
    private String createSnapshotKey(String persistenceId, long sequenceNr) {
        return persistenceId + "-snapshot-" + String.format("%020d", sequenceNr);
    }

    /**
     * 加载元数据索引
     */
    private void loadMetadataIndex() {
        String indexFile = snapshotDir + "/metadata-index.dat";
        File file = new File(indexFile);

        if (!file.exists()) {
            logger.info("Metadata index file not found, starting with empty index");
            return;
        }

        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) {
            @SuppressWarnings("unchecked")
            Map<String, SnapshotMetadata> loadedIndex = (Map<String, SnapshotMetadata>) ois.readObject();
            metadataIndex.putAll(loadedIndex);
            logger.info("Loaded metadata index with {} entries", metadataIndex.size());
        } catch (Exception e) {
            logger.error("Failed to load metadata index", e);
        }
    }

    /**
     * 保存元数据索引
     */
    private void saveMetadataIndex() {
        String indexFile = snapshotDir + "/metadata-index.dat";

        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(indexFile))) {
            oos.writeObject(new java.util.HashMap<>(metadataIndex));
            oos.flush();
        } catch (Exception e) {
            logger.error("Failed to save metadata index", e);
        }
    }

    @Override
    public void postStop() throws Exception {
        // 保存索引
        saveMetadataIndex();

        // 清理内存索引
        if (metadataIndex != null) {
            metadataIndex.clear();
        }

        logger.info("Real LMDB Snapshot Plugin stopped");
        super.postStop();
    }
}
